<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>canvas</title>
    <style>
      body {
        display: flex;
        flex-flow: row wrap;
        overflow: hidden;
        margin: 0;
        padding: 0;
        position: relative;
      }
      #root {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
      }
      h1 {
        text-align: center;
      }
      .cursor {
        cursor: pointer;
      }
      .flex {
        display: flex;
        flex-flow: row nowrap;
        align-items: center;
      }
      .flex span {
        flex: 0 0 150px;
      }
      .margin {
        margin: 10px 0;
      }
      .control-place {
        padding: 10px 0;
      }
    </style>
    <link rel="stylesheet" href="./video.css" />
  </head>
  <body>
    <div id="root">
      <h1>{{title}}</h1>
      <div id="control-place" class="control-place">
        <div class="flex margin">
          <span>开屏首帧图片来源(:model待完善):</span>
          <div>
            <label
              ><input
                type="radio"
                name="videoFirstFrame"
                value="1"
                @change="changeVideoFirstFrame"
              />视频首帧图片</label
            >
            <label
              ><input
                type="radio"
                name="videoFirstFrame"
                value="0"
                @change="changeVideoFirstFrame"
              />自定义图片</label
            >
          </div>
        </div>
        <div class="flex margin">
          <span>视频是否处理:</span>
          <div>
            <label
              ><input
                type="radio"
                name="videoHandle"
                value="1"
                @change="changeVideoHandle"
              />原视频</label
            >
            <label
              ><input
                type="radio"
                name="videoHandle"
                value="0"
                @change="changeVideoHandle"
              />处理后的视频</label
            >
          </div>
        </div>
      </div>
      <div>
        <div id="video-player" class="video-player">
          <video class="video-player_video" :src="videoUrl" @click="togglePlay"></video>
          <canvas @click="togglePlay"></canvas>
          <!-- <div class="video-player_bottom"></div> -->
          <div class="video-player_controls">
            <button
              :class="['video-player_control-btn', state.isPlaying ? 'pause' : 'play']"
              @click="togglePlay"
            ></button>
            <!-- timeFormat(state.currentTime) -->
            <span>{{state.currentTime}}</span>
            <div
              id="video-player_progress-bar"
              class="video-player_progress-bar cursor"
              @click="changeProgress"
            >
              <div
                class="video-player_progress"
                :style="{ width: state.progress + '%', height: '100%' }"
              ></div>
            </div>
            <span>{{state.duration}}</span>
            <div class="video-player_volume-bar">
              <button class="video-player_control-btn" @click="toggleVolume">
                音量
              </button>
              <input
                class="cursor"
                type="range"
                :if="state.isVolumeVisible"
                :model="state.volume"
                min="0"
                max="1"
                step="0.1"
                @change="changeVolume"
              />
            </div>
          </div>
        </div>
      </div>
    </div>
    <script type="module">
      // https://gitee.com/lin_peng118/lve-js/blob/master/index.js
      // 目前LveJs未上服务器，所以暂时不能直接使用
      import { Component } from "/core/index.js";
      const component = new Component({
        data: () => {
          return {
            player: {}, // 播放器盒子对象
            cvs: {}, // 画布
            video: {}, // 视频对象
            progressBar: {}, // 视频进度条父元素
            ctx: {}, // 画布对象
            title: "LveVideoJs",
            videoUrl: "./2e3d6097-b233-40af-88f4-a90a74a33fa0.mp4",
            state: {
              // 播放状态
              isPlaying: false,
              // 进度 0-100
              progress: 0,
              // 音量 0-1
              volume: 0.5,
              // 是否隐藏音量键
              isVolumeVisible: true,
              // 总时长
              duration: 0,
              // 目前播放时间
              currentTime: 0,
            },
            // 是否展示视频第一帧还是自定义图片
            displayFirstFrame: 1,
            // 视频是否处理
            videoHandle: 1,
          };
        },
        watch: {},
        mounted() {
          this.initVideo();
          this.showFrameOrCustomImage();
        },
        methods: {
          /** 更改开屏首帧图片来源 */
          changeVideoFirstFrame(e) {
            this.displayFirstFrame = Number(e.target.value);
          },
          changeVideoHandle(e) {
            this.videoHandle = Number(e.target.value);
          },
          /** 选择显示视频第一帧或者自定义图片 */
          showFrameOrCustomImage() {
            /** 下载完当前帧数据时触发 */
            this.video.addEventListener("loadeddata", () => {
              // 判断是否要显示第一帧
              if (this.displayFirstFrame) {
                // 设置视频当前时间为第一帧（0秒）
                this.video.currentTime = 0;
                // 暂停视频
                this.video.pause();
                setTimeout(() => {
                  this.paintCanvas();
                }, 50);
                // 展示自定义图片
              } else {
                const customImageURL = "./lddy.jpg"; // 根据需求设定
                this.paintCanvas(customImageURL);
              }
            });
            this.video.load();
          },
          /** 初始化视频 */
          initVideo() {
            this.player = document.querySelector("#video-player");
            this.video = document.querySelector("#video-player video");
            this.progressBar = document.querySelector(
              "#video-player_progress-bar"
            );
            /** 下载完元数据时候触发 */
            this.video.addEventListener("loadedmetadata", () => {
              this.cvs = document.querySelector("#video-player canvas");
              this.cvs.style.display = "block";
              this.ctx = this.cvs.getContext("2d");
              // 获取视频总时长
              this.state.duration = Math.ceil(this.video.duration);
              // 输出视频元素的宽度和高度
              this.cvs.height = this.video.offsetHeight;
              this.cvs.width =
                (this.video.videoWidth * this.video.offsetHeight) /
                this.video.videoHeight;
            });
            /** 视频播放结束时触发 */
            this.video.addEventListener("ended", () => {
              this.playerEnd();
            });
          },
          /** 开始播放视频 */
          start() {
            this.state.isPlaying = true;
            this.video.play();
            requestAnimationFrame(this.updateProgress.bind(this));
          },
          /** 暂停播放视频 */
          pause() {
            this.state.isPlaying = false;
            this.video.pause();
          },
          /** 更新当前视频进度 */
          updateProgress() {
            // 更新进度条
            this.state.progress =
              (this.video.currentTime / this.state.duration) * 100;
            // 更新当前时间
            this.state.currentTime = Math.ceil(this.video.currentTime);
            // 具体绘制操作
            if (!this.videoHandle) {
              this.cvs.style.display !== "block" &&
                (this.cvs.style.display = "block");
              this.video.style.opacity = 0;
              this.paintCanvas();
            } else {
              this.cvs.style.display = "none";
              this.video.style.opacity = 1;
            }
            // 当处于播放状态时，才执行RAF
            if (this.state.isPlaying) {
              requestAnimationFrame(this.updateProgress.bind(this));
            }
          },
          /** 绘制canvas */
          paintCanvas(from = "video") {
            const { width, height } = this.cvs;
            const canvas = document.createElement("canvas");
            canvas.width = width;
            canvas.height = height;
            const context = canvas.getContext("2d");
            new Promise((resolve, reject) => {
              if (from === "video") {
                context.drawImage(this.video, 0, 0, width, height);
                resolve();
              } else {
                const image = new Image();
                image.src = from;
                image.onload = () => {
                  context.drawImage(image, 0, 0, width, height);
                  resolve();
                };
              }
            }).then(() => {
              const data = context.getImageData(0, 0, width, height).data;
              const bl = 8;
              this.ctx.clearRect(0, 0, width, height);
              for (let i = 0; i < data.length; i += 4 * bl) {
                const x = parseInt(((i + 1) % (width * 4)) / 4);
                const y = parseInt(i / (width * 4));
                if (x % bl === 0 && y % bl === 0) {
                  const g = parseInt(
                    (data[i] + data[i + 1] + data[i + 2]) / 1.5
                  );
                  this.ctx.fillStyle = `rgba(${g}, ${g}, ${g}, ${data[i + 3]})`;
                  this.ctx.fillText("0", x, y);
                }
              }
            });
          },
          /** 开始/暂停操作 */
          togglePlay() {
            this.state.isPlaying = !this.state.isPlaying;
            if (this.state.isPlaying) {
              this.start();
            } else {
              this.pause();
            }
          },
          /** 显示隐藏音量进度条 */
          toggleVolume() {
            this.state.isVolumeVisible = !this.state.isVolumeVisible;
          },
          /** 改变音量进度 */
          changeVolume(e) {
            this.state.volume = this.video.volume = e.target.value;
          },
          /** 改变视频进度 */
          changeProgress(e) {
            // 先暂停, 更新进度和时间后，再开始
            this.pause();
            const percent = Math.round(
              (e.offsetX / this.progressBar.offsetWidth) * 100
            );
            this.state.progress = percent;
            this.state.currentTime = this.video.currentTime =
              (this.state.duration * percent) / 100;
            this.start();
          },
          /** 视频播放完毕 */
          playerEnd() {
            this.state.isPlaying = false;
            this.state.currentTime = this.video.currentTime =
              this.state.duration;
            this.state.progress = 100;
            this.video.removeEventListener("ended", () => {
              this.playerEnd();
            });
          },
          timeFormat(s) {
            const minute = parseInt((s / 60) % 60);
            const hour =
              parseInt(s / 3600) < 10
                ? "0" + parseInt(s / 3600)
                : parseInt(s / 3600);
            const second = Math.ceil(s % 60) > 59 ? 59 : Math.ceil(s % 60);
            return `${hour > 0 ? `${hour}:` : ""}${
              minute < 10 ? "0" + minute : minute
            }:${second < 10 ? "0" + second : second}`;
          },
        },
      });
      const player = component.mount("#root");
    </script>
  </body>
</html>
